package com.jinyang.jetpackdemo.activity

import android.Manifest
import android.app.Activity
import android.content.ContentResolver
import android.content.ContentUris
import android.content.Context
import android.content.Intent
import android.database.Cursor
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.provider.MediaStore
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.contentValuesOf
import androidx.lifecycle.lifecycleScope
import com.jinyang.jetpackdemo.R
import com.jinyang.jetpackdemo.util.*
import kotlinx.android.synthetic.main.activity_camerax.*
import kotlinx.android.synthetic.main.activity_storage.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import java.io.BufferedInputStream
import java.io.BufferedOutputStream
import java.io.File
import java.io.InputStream


class StorageActivity : AppCompatActivity() {
    private var uri: Uri? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_storage)
        //registerForActivityResult报错：
        // LifecycleOwner com.jinyang.jetpackdemo.activity.StorageActivity@9e231d7
        // is attempting to register while current state is RESUMED.
        // LifecycleOwners must call register before they are STARTED.
        //解决方法：You need to registerForActivityResult before onStart of the Activity.
        //选择图片，打开文档目录(常用于图片文件管理)
        val launcherOpenDocument =
            registerForActivityResult(ActivityResultContracts.OpenDocument()) {
                image_pic.setImageURI(it)
            }
        btn_open_document.setOnClickListener {
            launcherOpenDocument.launch(arrayOf("image/*"))
        }
        //系统拍照
        val launcherTakePicture = registerForActivityResult(ActivityResultContracts.TakePicture()) {
            if (it) {
                LjyToastUtil.getInstance().toast("take photo and save")
                image_pic.setImageURI(uri)
            }
        }
        btn_take_picture.setOnClickListener {
            val con = contentValuesOf()
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                con.put(MediaStore.Images.Media.RELATIVE_PATH, "DCIM/Pictures")
            } else {
                con.put(
                    MediaStore.Images.Media.DATA, File(
                        Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
                        "test.jpg"
                    ).absolutePath
                )
            }
            con.put(MediaStore.Images.Media.MIME_TYPE, "image/JPEG")
            uri = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, con)
            launcherTakePicture.launch(uri)
        }
        //直接选择图片(常用于一般图片选择需求)
        val launcherPICK =
            registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
                if (it.resultCode != Activity.RESULT_OK) return@registerForActivityResult
                it.data?.data?.let { uri -> image_pic.setImageURI(uri) }
            }
        btn_PICK.setOnClickListener {
            launcherPICK.launch(
                Intent(Intent.ACTION_PICK, null)
                    .setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*")
            )
        }
        val launcherSAF =
            registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
                if (result.resultCode == Activity.RESULT_OK) {
                    val uri: Uri? = result.data?.data
                    val fis: InputStream? = contentResolver.openInputStream(uri!!)
                    val bitmap = BitmapFactory.decodeStream(fis)
                    image_pic.setImageBitmap(bitmap)
                }
            }

        permissionsRequest(
            arrayOf(
                Manifest.permission.CAMERA,
                Manifest.permission.SEND_SMS,
                Manifest.permission.CALL_PHONE,
                Manifest.permission.READ_EXTERNAL_STORAGE,
                Manifest.permission.WRITE_EXTERNAL_STORAGE,
            )
        ) {
            if (it) {
                lifecycleScope.launch(Dispatchers.IO) {
                    //文件读写
//                        testFiles(this@StorageActivity)
                    //各种路径
                    testPaths()
                    //自带外部存储之共享存储空间和其它目录进行访问
                    //(1)访问共享存储空间分为两类文件：媒体文件和文档/其它文件
                    //1. 访问媒体文件
                    //1.1 直接构造路径
                    testShareMedia()
                    //1.2 通过MediaStore获取路径
//                        testMediaStore(this@StorageActivity)
                    //1.3 通过MediaStore获取Uri
                    testMediaStoreUri(this@StorageActivity)
                    //2. 访问文档和其它文件
                    //2.1 直接构造路径
                    //与媒体文件一样，可以直接构造路径访问
                    //2.2 通过SAF访问
                    btn_SAF.setOnClickListener {
                        launcherSAF.launch(
                            Intent(Intent.ACTION_OPEN_DOCUMENT)
                                .addCategory(Intent.CATEGORY_OPENABLE)
                                .setType("image/*")
                        )
                    }
                    //(2)其它目录
                    //1. 直接构造路径
                    testPublicFile()
                    //2.通过SAF访问: 与共享存储空间SAF访问方式一致。

                    //Android 10.0 访问方式变更:
                    // 上面访问方式的弊端，很多App能够直接在/sdcard/目录下创建目录/文件, 导致目录结构很乱，
                    // 而且"Clear storage"或者"Clear cache"或App卸载后，对应的目录并没有删除，于是就是遗留了很多"垃圾"文件，久而久之不处理，用户的存储空间越来越小
                    // 而且App可以随意修改其它目录下的文件，如修改别的App创建的文件等，不安全
                    //ps: 此处新建的目录不会被设置里的App存储用量统计，让用户"看起来"自己的App占用的存储空间很小
                    //Google在Android 10.0 引入Scoped Storage (作用域存储、分区存储、沙盒存储)
                    //1. App访问自身内部存储空间、访问外部存储空间-App私有目录不需要任何权限(与Android 10.0之前一致)
                    //2. 外部存储空间-共享存储空间、外部存储空间-其它目录 App无法通过路径直接访问，不能新建、删除、修改目录/文件等
                    //3. 外部存储空间-共享存储空间、外部存储空间-其它目录 需要通过Uri访问
                    //禁用分区存储的三种方法
                    //1. 设备系统<10.0
                    //2. targetSdkVersion<=28，分区存储功能就不会开启。
                    //3. 在AndroidManifest.xml 里application标签下添加： android:requestLegacyExternalStorage="true" 可禁用分区存储
                }
            } else {
                Toast.makeText(this, "我们需要相应的权限，请允许权限申请", Toast.LENGTH_LONG).show()
            }
        }


    }

    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        if (grantResults.isNotEmpty()) {
            LjyPermissionUtil.getInstance().onPermissionResult(this, requestCode, permissions, grantResults)
        }
    }
    private fun printFileName() {
        LjyLogUtil.d("操作文件...")
    }
    private fun requestPermission() {
        val permissions = arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.CALL_PHONE, Manifest.permission.CAMERA)
        if (LjyPermissionUtil.getInstance().hasPermissions(this@StorageActivity, permissions)) {
            printFileName()
        } else {
            val mRequestCode=1002
            LjyPermissionUtil.getInstance().requestPermission(
                this@StorageActivity,
                permissions,
                mRequestCode,
                object : LjyPermissionUtil.PermissionResultListener {
                    override fun onSuccess(requestCode: Int) {
                        if (requestCode == mRequestCode) {
                            printFileName()
                        }
                    }

                    override fun onDenied(deniedList: MutableList<String>) {
                        LjyToastUtil.getInstance().toast(  "权限被拒绝，将导致APP无法正常使用，请前往设置中修改")
                        for (it in deniedList) {
                            LjyLogUtil.d("deniedList:$it")
                        }
                    }

                    override fun onCancel(cancelList: MutableList<String>) {
                        LjyToastUtil.getInstance().toast( "取消了权限申请")
                        for (it in cancelList) {
                            LjyLogUtil.d("failList:$it")
                        }
                    }
                }
            )
        }
    }

    override fun onResume() {
        super.onResume()
        LjyLogUtil.d("onResume")
    }

    private fun testPublicFile() {
        val rootFile = Environment.getExternalStorageDirectory()
        val imagePath = rootFile.absolutePath + File.separator + "myDir"
        val myDir = File(imagePath)
        if (!myDir.exists()) {
            myDir.mkdir()
        }
    }

    private suspend fun testMediaStoreUri(context: Context) {
        LjyLogUtil.d("testMediaStoreUri")
        val contentResolver = context.contentResolver
        val cursor = contentResolver.query(
            MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
            null,
            null,
            null,
            null
        )
        while (cursor?.moveToNext() == true) {
            //获取唯一的id
            val id = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.MediaColumns._ID))
            //通过id构造Uri
            val uri: Uri =
                ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, id)
            LjyLogUtil.d("uri:$uri")
            lifecycleScope.launch(Dispatchers.Main) {
                image_pic.setImageURI(uri)
            }
            delay(100)
        }
        cursor?.close()
    }

    private suspend fun testMediaStore(context: Context) {
        LjyLogUtil.d("testMediaStore")
        val contentResolver: ContentResolver = context.contentResolver
        val cursor: Cursor? = contentResolver.query(
            MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
            null,
            null,
            null,
            null
        )
        while (cursor?.moveToNext() == true) {
            val index = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA)
            val imagePath: String = cursor.getString(index)
            LjyLogUtil.d("imagePath:$imagePath")
            val bitmap = BitmapFactory.decodeFile(imagePath)
            lifecycleScope.launch(Dispatchers.Main) {
                LjyLogUtil.d(Thread.currentThread().name)
                image_pic.setImageBitmap(bitmap)
            }
            delay(1000)
        }
        cursor?.close()
    }

    private fun testShareMedia() {
        LjyLogUtil.d("testShareMedia")
        //获取目录：/storage/emulated/0/
        val rootFile = Environment.getExternalStorageDirectory()
        val imagePath =
            rootFile.absolutePath + File.separator + Environment.DIRECTORY_PICTURES + File.separator + "123.jpg"
        // ---> /storage/emulated/0/Pictures/123.jpg
        val bitmap: Bitmap = BitmapFactory.decodeFile(imagePath)
        LjyLogUtil.d(Thread.currentThread().name)
        lifecycleScope.launch(Dispatchers.Main) {
            LjyLogUtil.d(Thread.currentThread().name)
            image_pic.setImageBitmap(bitmap)
        }
    }

    private fun testPaths() {
        LjyLogUtil.d("testPaths")
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            LjyLogUtil.d("dataDir: " + dataDir.absolutePath)
        }
        LjyLogUtil.d("getDir(\"test\"): " + getDir("test", MODE_PRIVATE).absolutePath)
        //通过上下文对象Context获取的，只要应用程序被卸载，目录下的文件都要被清空
        //1. 读写files目录下文件
        LjyLogUtil.d("filesDir: " + filesDir.absolutePath)
        //2. 读写cache目录下文件
        LjyLogUtil.d("cacheDir: " + cacheDir.absolutePath)
        //3. 读写code_cache目录下文件
        LjyLogUtil.d("codeCacheDir: " + codeCacheDir.absolutePath)
        //4. 读写数据库目录下文件
        LjyLogUtil.d("getDatabasePath: " + getDatabasePath("myDB").absolutePath)
        //以上为内部存储
        LjyLogUtil.d(Environment.getExternalStorageDirectory().absolutePath)
        LjyLogUtil.d(getExternalFilesDir(null)?.absolutePath)
        LjyLogUtil.d(getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)?.absolutePath)
        LjyLogUtil.d(getExternalFilesDir(Environment.DIRECTORY_PICTURES)?.absolutePath)
        //DIRECTORY_ALARMS 系统提醒铃声存放的标准目录。
        //DIRECTORY_DCIM 相机拍摄照片和视频的标准目录。
        //DIRECTORY_DOWNLOADS 下载的标准目录。
        //DIRECTORY_MOVIES 电影存放的标准目录。
        //DIRECTORY_MUSIC 音乐存放的标准目录。
        //DIRECTORY_NOTIFICATIONS 系统通知铃声存放的标准目录。
        //DIRECTORY_PICTURES 图片存放的标准目录
        //DIRECTORY_PODCASTS 系统广播存放的标准目录。
        //DIRECTORY_RINGTONES 系统铃声存放的标准目录。
        LjyLogUtil.d("externalCacheDir: " + externalCacheDir?.absolutePath)
        //返回android 安装包的完整路径，这个包是一个zip的压缩文件，它包括应用程序的代码和assets文件。
        LjyLogUtil.d("packageCodePath: $packageCodePath")
        //返回android 安装包的完整路径，这个包是一个ZIP的要锁文件，它包括应用程序的私有资源
        LjyLogUtil.d("packageResourcePath: $packageResourcePath")
        //返回应用程序的OBB文件目录（如果有的话），注意如果该应用程序没有任何OBB文件，这个目录是不存在的
        LjyLogUtil.d("obbDir: " + obbDir.absolutePath)
        //获得data的目录（/data）
        LjyLogUtil.d("Environment.getDataDirectory() : " + Environment.getDataDirectory()?.absolutePath)
        //获得下载缓存目录。（/cache）
        LjyLogUtil.d("Environment.getDownloadCacheDirectory() : " + Environment.getDownloadCacheDirectory()?.absolutePath)
        //获得系统主目录（/system）
        LjyLogUtil.d("Environment.getRootDirectory() : " + Environment.getRootDirectory()?.absolutePath)

        //Environment类去获取外部存储目录，在访问外部存储之前一定要先判断外部存储是否已经是可使用（已挂载&可使用）状态。
        // 并且需要在AndroidManifest.xml文件中添加外部存储读和写的权限。
        //Environment类中提供了几个静态常量用于标识外部存储的状态，这些状态都是String类型
        //可以通过静态方法getExternalStorageState()来获取外部存储的状态，如果程序需要在外部存储里面读写数据，必须要先判断。
        //MEDIA_BAD_REMOVAL 在没有挂载前存储媒体已经被移除。
        //MEDIA_CHECKING 正在检查存储媒体。
        //MEDIA_MOUNTED 存储媒体已经挂载，并且挂载点可读/写。
        //MEDIA_MOUNTED_READ_ONLY 存储媒体已经挂载，挂载点只读。
        //MEDIA_NOFS 存储媒体是空白或是不支持的文件系统。
        //MEDIA_REMOVED 存储媒体被移除。
        //MEDIA_SHARED 存储媒体正在通过USB共享。
        //MEDIA_UNMOUNTABLE 存储媒体无法挂载。
        //MEDIA_UNMOUNTED 存储媒体没有挂载。
        val isUsable = Environment.MEDIA_MOUNTED == Environment.getExternalStorageState()
                || !Environment.isExternalStorageRemovable()
        LjyLogUtil.d("isUsable:$isUsable")

        ///data/user/0/com.jinyang.jetpackdemo与/storage/emulated/0/Android/data/com.jinyang.jetpackdemo区别

    }

    /**
     * 1. 读写files目录下文件
     */
    private fun testFiles(context: Context) {
        //获取files根目录
        val fileDir: File = context.filesDir
        //获取文件
        val myFile = File(fileDir, "myFile")
        LjyLogUtil.d("myFile.absolutePath:${myFile.absolutePath}")
        //写数据
//        val fileOutputStream = FileOutputStream(myFile)
        val fileOutputStream = context.openFileOutput("myFile", MODE_APPEND)
        //MODE_APPEND 私有（只有创建此文件的程序能够使用，其他应用程序不能访问），在原有内容基础上增加数据
        //MODE_PRIVATE 私有，每次打开文件都会覆盖原来的内容
        //MODE_WORLD_READABLE 可以被其他应用程序读取
        //MODE_WORLD_WRITEABLE 可以被其他应用程序写入
        val bos = BufferedOutputStream(fileOutputStream)
        val writeContent = "hello world"
        for (i in 1..3) {
            bos.write(("$writeContent $i\n").toByteArray())
            bos.flush()
        }
        bos.close()
        //读数据
//        val fileInputStream = FileInputStream(myFile)
        val fileInputStream = context.openFileInput("myFile")
        val bis = BufferedInputStream(fileInputStream)
        val readContent = ByteArray(1024)
        var readLen = 0
        while (readLen != -1) {
            readLen = bis.read(readContent, 0, readContent.size)
            if (readLen > 0) {
                val content = String(readContent)
                LjyLogUtil.d("read content:" + content.substring(0, readLen))
            }
        }
        fileInputStream.close()
    }

    /**
     * 获取app缓存路径
     * @param context
     * @return
     */
    fun getCachePath(context: Context): String {
        val cachePath: String =
            if (Environment.MEDIA_MOUNTED == Environment.getExternalStorageState()
                || !Environment.isExternalStorageRemovable()
            ) {
                //外部存储可用
                context.externalCacheDir!!.path
            } else {
                //外部存储不可用
                context.cacheDir.path
            }
        return cachePath
    }
}